﻿using Cysharp.Threading.Tasks;
using System;
using System.Collections.Generic;
using UniRx;
using UnityEngine;

/// <summary>
/// 流式输入管理器
/// (从存档中读取未添加，更新修改后的键位未添加)
/// </summary>
public class InputManager : GameFrameClassBase
{
    private static readonly TimeSpan SKIP_TIME = TimeSpan.FromSeconds(0.5d);
    private static readonly TimeSpan INTERVAL_TIME = TimeSpan.FromSeconds(0.1d);
    private static Dictionary<KeyCode, KeyStream> s_KeyStreams = new Dictionary<KeyCode, KeyStream>();
    private static List<KeyConfig> s_KeyConfigs = new List<KeyConfig>();

    private static readonly List<string> s_AxisCommonds = new List<string>()
    {
        "axis_horizontal_positive",
        "axis_horizontal_negative",
        "axis_vertical_positive",
        "axis_vertical_negative"
    };
    private static Dictionary<string, KeyCode> s_AxisDict = new Dictionary<string, KeyCode>();

    /// <summary>
    /// 水平轴
    /// </summary>
    public static IObservable<float> horizontal
    {
        get
        {
            if (s_Horizontal == null)
            {
                if (!s_AxisDict.TryGetValue(s_AxisCommonds[0], out var posiKey))
                {
                    Debug.LogError($"键位初始化失败：{s_AxisCommonds[0]} 未配置");
                    return null;
                }
                if (!s_AxisDict.TryGetValue(s_AxisCommonds[1], out var negaKey))
                {
                    Debug.LogError($"键位初始化失败：{s_AxisCommonds[1]} 未配置");
                    return null;
                }
                s_Horizontal = Observable.EveryGameObjectUpdate().ToFloat().Select(_ =>
                {
                    var posi = Input.GetKey(posiKey);
                    var nega = Input.GetKey(negaKey);
                    if (!(posi ^ nega)) return 0f;
                    else if (posi) return 1f;
                    else return -1f;
                });
            }
            return s_Horizontal;
        }
    }
    private static IObservable<float> s_Horizontal;

    /// <summary>
    /// 垂直轴
    /// </summary>
    public static IObservable<float> vertical
    {
        get
        {
            if (s_Vertical == null)
            {
                if (!s_AxisDict.TryGetValue(s_AxisCommonds[2], out var posiKey))
                {
                    Debug.LogError($"键位初始化失败：{s_AxisCommonds[2]} 未配置");
                    return null;
                }
                if (!s_AxisDict.TryGetValue(s_AxisCommonds[3], out var negaKey))
                {
                    Debug.LogError($"键位初始化失败：{s_AxisCommonds[3]} 未配置");
                    return null;
                }
                s_Vertical = Observable.EveryGameObjectUpdate().ToFloat().Select(_ =>
                {
                    var posi = Input.GetKey(posiKey);
                    var nega = Input.GetKey(negaKey);
                    if (!posi ^ nega) return 0f;
                    else if (posi) return 1f;
                    else return -1f;
                });
            }
            return s_Vertical;
        }
    }
    private static IObservable<float> s_Vertical;

    public override List<Type> dependences => new List<Type> { typeof(ConfigLoader) };

    public override UniTask InitAsync()
    {
        LoadKeyConfigs();
        InitKeyStreams();
        return UniTask.CompletedTask;
    }

    public static KeyStream FindSteam(KeyCode key)
    {
        if (!s_KeyStreams.TryGetValue(key, out var stream))
        {
            Debug.LogError($"键位查找失败：找不到 {key} 的事件流");
        }
        return stream;
    }

    private static void LoadKeyConfigs()
    {
        var configs = ConfigLoader.GetConfig<KeyConfig>();
        if (configs == null) return;
        foreach (var cfg in configs)
        {
            if (IsConfigEmpty(cfg)) continue;
            s_KeyConfigs.Add(cfg);
        }
    }

    private static bool IsConfigEmpty(KeyConfig config)
    {
        return config == null || config.key == null || (config.down == null && config.up == null && config.press == null && config.longpress == null);
    }

    private static void InitKeyStreams()
    {
        foreach (var cfg in s_KeyConfigs)
        {
            var keyStream = new KeyStream();
            keyStream.key = cfg.key.Parse<KeyCode>();

            if (!cfg.down.IsEmpty())
            {
                if (!s_AxisCommonds.Contains(cfg.down))
                {
                    keyStream.down = Observable.EveryGameObjectUpdate().AsUnitObservable().Where(_ => Input.GetKeyDown(keyStream.key));
                    CommandManager.RegisterStream(cfg.down, keyStream.down);
                }
                else
                {
                    s_AxisDict.Add(cfg.down, cfg.key.Parse<KeyCode>());
                }
            }
            if (!cfg.up.IsEmpty())
            {
                if (!s_AxisCommonds.Contains(cfg.up))
                {
                    keyStream.up = Observable.EveryGameObjectUpdate().AsUnitObservable().Where(_ => Input.GetKeyUp(keyStream.key));
                    CommandManager.RegisterStream(cfg.up, keyStream.up);
                }
                else
                {
                    s_AxisDict.Add(cfg.up, cfg.key.Parse<KeyCode>());
                }
            }
            if (!cfg.press.IsEmpty())
            {
                if (!s_AxisCommonds.Contains(cfg.press))
                {
                    keyStream.press = Observable.EveryGameObjectUpdate().AsUnitObservable().Where(_ => Input.GetKey(keyStream.key));
                    CommandManager.RegisterStream(cfg.press, keyStream.press);
                }
                else
                {
                    s_AxisDict.Add(cfg.press, cfg.key.Parse<KeyCode>());
                }
            }
            if (!cfg.longpress.IsEmpty())
            {
                if (!s_AxisCommonds.Contains(cfg.longpress))
                {
                    keyStream.longpress = Observable.EveryGameObjectUpdate().AsUnitObservable().Where(_ => Input.GetKey(keyStream.key)).Suppressor(Observable.EveryGameObjectUpdate().Where(_ => Input.GetKeyDown(keyStream.key)), SKIP_TIME).Where(_ => !Input.GetKeyDown(keyStream.key)).ThrottleFirst(INTERVAL_TIME);
                    CommandManager.RegisterStream(cfg.longpress, keyStream.longpress);
                }
                else
                {
                    s_AxisDict.Add(cfg.longpress, cfg.key.Parse<KeyCode>());
                }
            }

            s_KeyStreams[keyStream.key] = keyStream;
        }
    }
}